/*******************************************************************************
 * Copyright (c) 2009 Kevin Bierhoff and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     Kevin Bierhoff - initial API and implementation
 *******************************************************************************/
package com.googlecode.twelfdevtools.model;

import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import org.eclipse.core.filebuffers.ITextFileBuffer;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.IDocument;

import com.googlecode.twelfdevtools.util.BufferUtil;
import com.googlecode.twelfdevtools.util.BufferUtil.BufferAction;

/**
 * @author kevin
 *
 */
class TwelfProjectImpl implements ITwelfProject {
	
	/** Underlying project resource */
	private final IProject resource;
	/** Cached elf files in the project, lazily populated */
	private Map<IFile, AbstractElfFileModel> files;
	
	TwelfProjectImpl(IProject resource) {
		this.resource = resource;
	}

	public ITwelfModelElement getParent() {
		// no parent
		return null;
	}
	
	public IProject getResource() {
		return resource;
	}

	/** 
	 * Returns the predecessor of the given file in the build order
	 * @param elfFile Must be a file in this project
	 * @return
	 * @throws IllegalStateException if the given file is not in this project
	 */
	AbstractElfFileModel getPredecessor(AbstractElfFileModel elfFile) {
		final IFile file = elfFile.getResource();
		if (files == null) {
			// configuration not read yet
			files = new LinkedHashMap<IFile, AbstractElfFileModel>();
			IFile configFile = TwelfModel.getConfigFile(resource);
			try {
				for (IFile f : BufferUtil.doInBuffer(
						configFile, new BuildContextAction(resource))) {
					files.put(f, null);
				}
				// put in the elf file we have already
				files.put(file, elfFile);
			} catch (CoreException e) {
				e.printStackTrace();
				files = null;
				return null;
			}
		}
		
		IFile pred = null;
		AbstractElfFileModel result = null;
		for (Map.Entry<IFile, AbstractElfFileModel> f : files.entrySet()) {
			if (f.getKey().equals(file)) {
				if (pred == null)
					// no predecessor
					return null;
				if (result == null) {
					// predecessor not initialized
					result = new FileBasedFileModel(this, pred);
					// modifition ok since we return right after
					files.put(pred, result);
				}
				// return predecessor
				return result;
			}
			else {
				pred = f.getKey();
				result = f.getValue();
			}
		}
		throw new IllegalStateException("" + elfFile.getResource() + 
				" not in " + resource);
	}
	
	private static final class BuildContextAction 
	implements BufferAction<List<IFile>> {
		
		private final IFile forFile;
		// TODO don't need project if we make this an inner class
		private final IProject forProject;
		
		public BuildContextAction(IProject forProject) {
			this(forProject, null);
		}
		
		public BuildContextAction(IProject forProject, IFile forFile) {
			this.forProject = forProject;
			this.forFile = forFile;
		}

		public List<IFile> bufferAction(IFile f, ITextFileBuffer buffer) {
			IDocument doc = buffer.getDocument();
			// number of lines is an upper bound on how many files we can return
			List<IFile> result = new ArrayList<IFile>(doc.getNumberOfLines());
			try {
				for(int i = 0; i < doc.getNumberOfLines(); ++i) {
					String line = doc.get(doc.getLineOffset(i), doc.getLineLength(i));
					line = line.trim();
					if(line.isEmpty())
						continue;
					IFile file = forProject.getFile(line);
					if(!file.exists())
						throw new IllegalStateException("Configured file not found: " + line);
					result.add(file);
					if (file.equals(forFile))
						return result;
				}
			} catch (BadLocationException e) {
				// this shouldn't happen
				e.printStackTrace();
				return null;
			}
			if (forFile != null)
				throw new IllegalStateException("Requested file not found: " + forFile);
			else
				// need all files
				return result;
		}
	}

}
